PIO can be as simple as storing a variable into a bus address (as passed to the pfxedtinit() entry point). Example 18-2 displays fragmentary code of a hypothetical character device driver for a GIO device that controls a printer. This pfxwrite() entry point copies data from the user address space to device memory using the uiomove() function (see "Transferring Data Through a uio_t Object"). Then it stores an explicit command in the device to start it, and sleeps until the device interrupts.
Example 18-2 : Hypothetical PIO Routine for GIO
/* device write routine entry point (for character devices)*/ int hypoth_write(dev_t dev, uio_t *uio) { int unit = geteminor(dev)&1; int size, err=0, s; /* while there is data to transfer */ while((size=uio->uio_resid) > 0) { /* Transfer no more than GBD_MEMSIZE bytes */ size = size < GBD_MEMSIZE ? size : GBD_MEMSIZE; /* decrements size, updates uio fields, copies data */ if(err=uiomove(gbd_memory[unit], size, UIO_WRITE, uio)) break; /* prevent interrupts until we sleep */ s = splgio1(); /* Transfer is complete; start output */ gbd_device[unit]->count = size; gbd_device[unit]->command = GBD_GO; gbd_state[unit] = GBD_SLEEPING; while (gbd_state[unit] != GBD_DONE) { sleep(&gbd_state[unit], PRIBIO); } /* restore the interrupt level after waking up */ splx(s); } return err; }An expression like gdb_device[unit]->command=GBD_GO represents storing a command value in a device register. Presumably the gdb_device array is set up with a device address for each slot in the pfxedtinit() entry point.
The code in Example 18-2 uses splgio1() to block an interrupt from occurring after it has started the device in operation and before it has entered the blocked state using sleep(). If this was not done, there is a small window of time during which an interrupt could occur and be handled before the upper-half routine had begun sleeping. Then it would sleep forever.
An alternate way to handle this same situation in a multiprocessor system is to use a mutual-exclusion lock to get exclusive use of the device registers, and a synchronization variable to wait for the interrupt (see "Using Synchronization Variables").